﻿using System;
using System.Data;
using l1cs.server.model.Instance;
using l1cs.server.model.map;
using l1cs.server.templates;
using l1cs.server.utils;
using MySql.Data.MySqlClient;
using System.Collections;

namespace l1cs.server.datatables
{
    /// <summary>
    /// charactersテーブルの操作をするクラス。
    /// </summary>
    public class CharacterTable
    {/*TTOK*/
        private static CharacterTable _instance;

        private static Logger _log = Logger.getLogger( typeof( CharacterTable ).FullName );

        /// <summary>スレッドセーフなキャラクターハッシュ</summary>
        private Hashtable _charNameList = Hashtable.Synchronized( new Hashtable() );

        private CharacterTable() { }

        public static CharacterTable getInstance()
        {
            if ( _instance == null )
            {
                _instance = new CharacterTable();
            }
            return _instance;
        }

        /// <summary>
        /// 新しいキャラクターをDBに書き込む。
        /// </summary>
        /// <param name="pc">プレイヤー情報</param>
        public void storeNewCharacter( L1PcInstance pc )
        {/*TTOK*/
            lock ( pc )
            {
                String sqlstr = @"INSERT INTO characters SET 
                                    account_name	=	?1, 
                                    objid	        =	?2, 
                                    char_name	    =	?3, 
                                    level	        =	?4, 
                                    HighLevel	    =	?5, 
                                    Exp	            =	?6, 
                                    MaxHp	        =	?7, 
                                    CurHp	        =	?8, 
                                    MaxMp	        =	?9, 
                                    CurMp	        =	?10, 
                                    Ac	            =	?11, 
                                    Str	            =	?12, 
                                    Con	            =	?13, 
                                    Dex	            =	?14, 
                                    Cha	            =	?15, 
                                    Intel	        =	?16, 
                                    Wis	            =	?17, 
                                    Status	        =	?18, 
                                    Class	        =	?19, 
                                    Sex	            =	?20, 
                                    Type	        =	?21, 
                                    Heading	        =	?22, 
                                    LocX	        =	?23, 
                                    LocY	        =	?24, 
                                    MapID	        =	?25, 
                                    Food	        =	?26, 
                                    Lawful	        =	?27, 
                                    Title	        =	?28, 
                                    ClanID	        =	?29, 
                                    Clanname	    =	?30, 
                                    ClanRank	    =	?31, 
                                    BonusStatus	    =	?32, 
                                    ElixirStatus	=	?33, 
                                    ElfAttr	        =	?34, 
                                    PKcount	        =	?35, 
                                    PkCountForElf	=	?36, 
                                    ExpRes	        =	?37, 
                                    PartnerID	    =	?38, 
                                    AccessLevel	    =	?39, 
                                    OnlineStatus	=	?40, 
                                    HomeTownID	    =	?41, 
                                    Contribution	=	?42, 
                                    Pay	            =	?43, 
                                    HellTime	    =	?44, 
                                    Banned	        =	?45, 
                                    Karma	        =	?46, 
                                    LastPk	        =	?47, 
                                    LastPkForElf	=	?48, 
                                    DeleteTime	    =	?49";
                using ( MySqlWrapper wrapper = new MySqlWrapper() )
                {
                    MySqlCommand cmd = new MySqlCommand( sqlstr );
                    int hp = pc.getCurrentHp();
                    if ( hp < 1 )
                    {
                        hp = 1;
                    }
                    cmd.Parameters.Add( "?1", MySqlDbType.String ).Value = pc.getAccountName();
                    cmd.Parameters.Add( "?2", MySqlDbType.Int24 ).Value = pc.getId();
                    cmd.Parameters.Add( "?3", MySqlDbType.String ).Value = pc.getName();
                    cmd.Parameters.Add( "?4", MySqlDbType.Int24 ).Value = pc.getLevel();
                    cmd.Parameters.Add( "?5", MySqlDbType.Int24 ).Value = pc.getHighLevel();
                    cmd.Parameters.Add( "?6", MySqlDbType.Int24 ).Value = pc.getExp();
                    cmd.Parameters.Add( "?7", MySqlDbType.Int24 ).Value = pc.getBaseMaxHp();
                    cmd.Parameters.Add( "?8", MySqlDbType.Int24 ).Value = hp;
                    cmd.Parameters.Add( "?9", MySqlDbType.Int24 ).Value = pc.getBaseMaxMp();
                    cmd.Parameters.Add( "?10", MySqlDbType.Int24 ).Value = pc.getCurrentMp();
                    cmd.Parameters.Add( "?11", MySqlDbType.Int24 ).Value = pc.getAc();
                    cmd.Parameters.Add( "?12", MySqlDbType.Int24 ).Value = pc.getBaseStr();
                    cmd.Parameters.Add( "?13", MySqlDbType.Int24 ).Value = pc.getBaseCon();
                    cmd.Parameters.Add( "?14", MySqlDbType.Int24 ).Value = pc.getBaseDex();
                    cmd.Parameters.Add( "?15", MySqlDbType.Int24 ).Value = pc.getBaseCha();
                    cmd.Parameters.Add( "?16", MySqlDbType.Int24 ).Value = pc.getBaseInt();
                    cmd.Parameters.Add( "?17", MySqlDbType.Int24 ).Value = pc.getBaseWis();
                    cmd.Parameters.Add( "?18", MySqlDbType.Int24 ).Value = pc.getCurrentWeapon();
                    cmd.Parameters.Add( "?19", MySqlDbType.Int24 ).Value = pc.getClassId();
                    cmd.Parameters.Add( "?20", MySqlDbType.Int24 ).Value = pc.get_sex();
                    cmd.Parameters.Add( "?21", MySqlDbType.Int24 ).Value = pc.getType();
                    cmd.Parameters.Add( "?22", MySqlDbType.Int24 ).Value = pc.getHeading();
                    cmd.Parameters.Add( "?23", MySqlDbType.Int24 ).Value = pc.getX();
                    cmd.Parameters.Add( "?24", MySqlDbType.Int24 ).Value = pc.getY();
                    cmd.Parameters.Add( "?25", MySqlDbType.Int24 ).Value = pc.getMapId();
                    cmd.Parameters.Add( "?26", MySqlDbType.Int24 ).Value = pc.get_food();
                    cmd.Parameters.Add( "?27", MySqlDbType.Int24 ).Value = pc.getLawful();
                    cmd.Parameters.Add( "?28", MySqlDbType.String ).Value = pc.getTitle();
                    cmd.Parameters.Add( "?29", MySqlDbType.Int24 ).Value = pc.getClanid();
                    cmd.Parameters.Add( "?30", MySqlDbType.String ).Value = pc.getClanname();
                    cmd.Parameters.Add( "?31", MySqlDbType.Int24 ).Value = pc.getClanRank();
                    cmd.Parameters.Add( "?32", MySqlDbType.Int24 ).Value = pc.getBonusStats();
                    cmd.Parameters.Add( "?33", MySqlDbType.Int24 ).Value = pc.getElixirStats();
                    cmd.Parameters.Add( "?34", MySqlDbType.Int24 ).Value = pc.getElfAttr();
                    cmd.Parameters.Add( "?35", MySqlDbType.Int24 ).Value = pc.get_PKcount();
                    cmd.Parameters.Add( "?36", MySqlDbType.Int24 ).Value = pc.getPkCountForElf();
                    cmd.Parameters.Add( "?37", MySqlDbType.Int24 ).Value = pc.getExpRes();
                    cmd.Parameters.Add( "?38", MySqlDbType.Int24 ).Value = pc.getPartnerId();
                    cmd.Parameters.Add( "?39", MySqlDbType.Int24 ).Value = pc.getAccessLevel();
                    cmd.Parameters.Add( "?40", MySqlDbType.Int24 ).Value = pc.getOnlineStatus();
                    cmd.Parameters.Add( "?41", MySqlDbType.Int24 ).Value = pc.getHomeTownId();
                    cmd.Parameters.Add( "?42", MySqlDbType.Int24 ).Value = pc.getContribution();
                    cmd.Parameters.Add( "?43", MySqlDbType.Int24 ).Value = 0;
                    cmd.Parameters.Add( "?44", MySqlDbType.Int24 ).Value = pc.getHellTime();
                    cmd.Parameters.Add( "?45", MySqlDbType.Int24 ).Value = pc.isBanned();
                    cmd.Parameters.Add( "?46", MySqlDbType.Int24 ).Value = pc.getKarma();
                    cmd.Parameters.Add( "?47", MySqlDbType.Int24 ).Value = pc.getLastPk();
                    cmd.Parameters.Add( "?48", MySqlDbType.Int24 ).Value = pc.getLastPkForElf();
                    cmd.Parameters.Add( "?49", MySqlDbType.Int24 ).Value = pc.getDeleteTime();
                    wrapper.ExecNonQuery( cmd );
                }
                String name = pc.getName().ToLower();
                if ( !_charNameList.ContainsKey( name ) )
                {
                    L1CharName cn = new L1CharName();
                    cn.setName( name );
                    cn.setId( pc.getId() );
                    _charNameList[name] = cn;
                }
                _log.finest( "storeNewCharacter" );
            }
        }

        /// <summary>
        /// プライヤーの状態を更新する。
        /// </summary>
        /// <param name="pc">プレイヤー情報</param>
        public void storeCharacter( L1PcInstance pc )
        {/*TTOK*/
            lock ( pc )
            {
                String sqlstr = @"UPDATE characters SET 
                                     level =		?1,
                                     HighLevel =	?2,
                                     Exp =		    ?3,
                                     MaxHp =		?4,
                                     CurHp =		?5,
                                     MaxMp =		?6,
                                     CurMp =		?7,
                                     Ac =		    ?8,
                                     Str =		    ?9,
                                     Con =		    ?10,
                                     Dex =		    ?11,
                                     Cha =		    ?12,
                                     Intel =		?13,
                                     Wis =		    ?14,
                                     Status =		?15,
                                     Class =		?16,
                                     Sex =		    ?17,
                                     Type =		    ?18,
                                     Heading =		?19,
                                     LocX =		    ?20,
                                     LocY =		    ?21,
                                     MapID =		?22,
                                     Food =		    ?23,
                                     Lawful =		?24,
                                     Title =		?25,
                                     ClanID =		?26,
                                     Clanname =		?27,
                                     ClanRank =		?28,
                                     BonusStatus =	?29,
                                     ElixirStatus =	?30,
                                     ElfAttr =		?31,
                                     PKcount =		?32,
                                     PkCountForElf =?33,
                                     ExpRes =		?34,
                                     PartnerID =	?35,
                                     AccessLevel =	?36,
                                     OnlineStatus =	?37,
                                     HomeTownID =	?38,
                                     Contribution =	?39,
                                     HellTime =		?40,
                                     Banned =		?41,
                                     Karma =		?42,
                                     LastPk =		?43,
                                     LastPkForElf =	?44,
                                     DeleteTime =	?45	
                                     WHERE objid =	?46";
                using ( MySqlWrapper wrapper = new MySqlWrapper() )
                {
                    int hp = pc.getCurrentHp();
                    if ( hp < 1 )
                    {
                        hp = 1;
                    }
                    MySqlCommand cmd = new MySqlCommand( sqlstr );
                    cmd.Parameters.Add( "?1", MySqlDbType.Int24 ).Value = pc.getLevel();
                    cmd.Parameters.Add( "?2", MySqlDbType.Int24 ).Value = pc.getHighLevel();
                    cmd.Parameters.Add( "?3", MySqlDbType.Int24 ).Value = pc.getExp();
                    cmd.Parameters.Add( "?4", MySqlDbType.Int24 ).Value = pc.getBaseMaxHp();
                    cmd.Parameters.Add( "?5", MySqlDbType.Int24 ).Value = hp;
                    cmd.Parameters.Add( "?6", MySqlDbType.Int24 ).Value = pc.getBaseMaxMp();
                    cmd.Parameters.Add( "?7", MySqlDbType.Int24 ).Value = pc.getCurrentMp();
                    cmd.Parameters.Add( "?8", MySqlDbType.Int24 ).Value = pc.getAc();
                    cmd.Parameters.Add( "?9", MySqlDbType.Int24 ).Value = pc.getBaseStr();
                    cmd.Parameters.Add( "?10", MySqlDbType.Int24 ).Value = pc.getBaseCon();
                    cmd.Parameters.Add( "?11", MySqlDbType.Int24 ).Value = pc.getBaseDex();
                    cmd.Parameters.Add( "?12", MySqlDbType.Int24 ).Value = pc.getBaseCha();
                    cmd.Parameters.Add( "?13", MySqlDbType.Int24 ).Value = pc.getBaseInt();
                    cmd.Parameters.Add( "?14", MySqlDbType.Int24 ).Value = pc.getBaseWis();
                    cmd.Parameters.Add( "?15", MySqlDbType.Int24 ).Value = pc.getCurrentWeapon();
                    cmd.Parameters.Add( "?16", MySqlDbType.Int24 ).Value = pc.getClassId();
                    cmd.Parameters.Add( "?17", MySqlDbType.Int24 ).Value = pc.get_sex();
                    cmd.Parameters.Add( "?18", MySqlDbType.Int24 ).Value = pc.getType();
                    cmd.Parameters.Add( "?19", MySqlDbType.Int24 ).Value = pc.getHeading();
                    cmd.Parameters.Add( "?20", MySqlDbType.Int24 ).Value = pc.getX();
                    cmd.Parameters.Add( "?21", MySqlDbType.Int24 ).Value = pc.getY();
                    cmd.Parameters.Add( "?22", MySqlDbType.Int24 ).Value = pc.getMapId();
                    cmd.Parameters.Add( "?23", MySqlDbType.Int24 ).Value = pc.get_food();
                    cmd.Parameters.Add( "?24", MySqlDbType.Int24 ).Value = pc.getLawful();
                    cmd.Parameters.Add( "?25", MySqlDbType.String ).Value = pc.getTitle();
                    cmd.Parameters.Add( "?26", MySqlDbType.Int24 ).Value = pc.getClanid();
                    cmd.Parameters.Add( "?27", MySqlDbType.String ).Value = pc.getClanname();
                    cmd.Parameters.Add( "?28", MySqlDbType.Int24 ).Value = pc.getClanRank();
                    cmd.Parameters.Add( "?29", MySqlDbType.Int24 ).Value = pc.getBonusStats();
                    cmd.Parameters.Add( "?30", MySqlDbType.Int24 ).Value = pc.getElixirStats();
                    cmd.Parameters.Add( "?31", MySqlDbType.Int24 ).Value = pc.getElfAttr();
                    cmd.Parameters.Add( "?32", MySqlDbType.Int24 ).Value = pc.get_PKcount();
                    cmd.Parameters.Add( "?33", MySqlDbType.Int24 ).Value = pc.getPkCountForElf();
                    cmd.Parameters.Add( "?34", MySqlDbType.Int24 ).Value = pc.getExpRes();
                    cmd.Parameters.Add( "?35", MySqlDbType.Int24 ).Value = pc.getPartnerId();
                    cmd.Parameters.Add( "?36", MySqlDbType.Int24 ).Value = pc.getAccessLevel();
                    cmd.Parameters.Add( "?37", MySqlDbType.Int24 ).Value = pc.getOnlineStatus();
                    cmd.Parameters.Add( "?38", MySqlDbType.Int24 ).Value = pc.getHomeTownId();
                    cmd.Parameters.Add( "?39", MySqlDbType.Int24 ).Value = pc.getContribution();
                    cmd.Parameters.Add( "?40", MySqlDbType.Int24 ).Value = pc.getHellTime();
                    cmd.Parameters.Add( "?41", MySqlDbType.Int24 ).Value = pc.isBanned();
                    cmd.Parameters.Add( "?42", MySqlDbType.Int24 ).Value = pc.getKarma();
                    cmd.Parameters.Add( "?43", MySqlDbType.Int24 ).Value = pc.getLastPk();
                    cmd.Parameters.Add( "?44", MySqlDbType.Int24 ).Value = pc.getLastPkForElf();
                    cmd.Parameters.Add( "?45", MySqlDbType.Int24 ).Value = pc.getDeleteTime();
                    cmd.Parameters.Add( "?46", MySqlDbType.Int24 ).Value = pc.getId();
                    wrapper.ExecNonQuery( cmd );
                }
                _log.finest( "storeCharacter: " + pc.getName() );
                _log.finest( Environment.StackTrace );
            }
        }

        /// <summary>
        /// キャラクターを削除する。
        /// </summary>
        /// <param name="accountName">アカウント名</param>
        /// <param name="charName">キャラクター名</param>
        public void deleteCharacter( String accountName, String charName )
        {/*TTOK*/
            // 多分、同期は必要ない
            String sqlstr = @"SELECT * FROM characters WHERE 
                                account_name=?1 AND char_name=?2";
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                MySqlCommand cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.String ).Value = accountName;
                cmd.Parameters.Add( "?2", MySqlDbType.String ).Value = charName;
                DataTable dt = wrapper.GetDataTable( cmd );
                if ( dt.Rows.Count == 0 )
                {
                    /*
                     * SELECTが値を返していない
                     * 存在しないか、あるいは別のアカウントが所有している
                     * キャラクター名が指定されたということになる。
                     */
                    _log.warning( String.Format(
                        "invalid delete char request: account={0} char={1}",
                        accountName, charName ) );
                    return;
                }
                String inSqlstr = "(SELECT objid FROM characters WHERE char_name = ?1)";
                sqlstr = "DELETE FROM character_buddys WHERE char_id IN " + inSqlstr;
                cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.String ).Value = charName;
                wrapper.ExecNonQuery( cmd );
                sqlstr = "DELETE FROM character_buff WHERE char_obj_id IN " + inSqlstr;
                cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.String ).Value = charName;
                wrapper.ExecNonQuery( cmd );
                sqlstr = "DELETE FROM character_config WHERE object_id IN " + inSqlstr;
                cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.String ).Value = charName;
                wrapper.ExecNonQuery( cmd );
                sqlstr = "DELETE FROM character_items WHERE char_id IN " + inSqlstr;
                cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.String ).Value = charName;
                wrapper.ExecNonQuery( cmd );
                sqlstr = "DELETE FROM character_quests WHERE char_id IN " + inSqlstr;
                cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.String ).Value = charName;
                wrapper.ExecNonQuery( cmd );
                sqlstr = "DELETE FROM character_skills WHERE char_obj_id IN " + inSqlstr;
                cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.String ).Value = charName;
                wrapper.ExecNonQuery( cmd );
                sqlstr = "DELETE FROM character_teleport WHERE char_id IN " + inSqlstr;
                cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.String ).Value = charName;
                wrapper.ExecNonQuery( cmd );
                sqlstr = "DELETE FROM characters WHERE char_name=?1";
                cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.String ).Value = charName;
                wrapper.ExecNonQuery( cmd );
            }
            if ( _charNameList.ContainsKey( charName ) )
            {
                _charNameList.Remove( charName );
            }
            _log.finest( "deleteCharacter" );
        }

        /// <summary>
        /// プレイヤー情報をデータベースから読み込む。
        /// </summary>
        /// <param name="charName">キャラクター名</param>
        /// <returns>プレイヤー情報</returns>
        public L1PcInstance restoreCharacter( String charName )
        {/*TTOK*/
            L1PcInstance pc = null;
            DataTable dt;
            String sqlstr = "SELECT * FROM characters WHERE char_name=?1";//, charName );
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                MySqlCommand cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.String ).Value = charName;
                dt = wrapper.GetDataTable( cmd );
            }
            if ( dt.Rows.Count == 0 )
            {
                /*
                 * SELECTが結果を返さなかった。
                 */
                return null;
            }
            pc = new L1PcInstance();
            pc.setAccountName( Convert.ToString( dt.Rows[0]["account_name"] ) );
            pc.setId( SqlUtils.toInt( dt.Rows[0]["objid"] ) );
            pc.setName( Convert.ToString( dt.Rows[0]["char_name"] ) );
            pc.setHighLevel( SqlUtils.toInt( dt.Rows[0]["HighLevel"] ) );
            pc.setExp( SqlUtils.toInt( dt.Rows[0]["Exp"] ) );
            pc.addBaseMaxHp( SqlUtils.toShort( dt.Rows[0]["MaxHp"] ) );
            short currentHp = SqlUtils.toShort( dt.Rows[0]["CurHp"] );
            if ( currentHp < 1 )
            {
                currentHp = 1;
            }
            pc.setCurrentHpDirect( currentHp );
            pc.setDead( false );
            pc.setStatus( 0 );
            pc.addBaseMaxMp( SqlUtils.toShort( dt.Rows[0]["MaxMp"] ) );
            pc.setCurrentMpDirect( SqlUtils.toShort( dt.Rows[0]["CurMp"] ) );
            pc.addBaseStr( Convert.ToByte( dt.Rows[0]["Str"] ) );
            pc.addBaseCon( Convert.ToByte( dt.Rows[0]["Con"] ) );
            pc.addBaseDex( Convert.ToByte( dt.Rows[0]["Dex"] ) );
            pc.addBaseCha( Convert.ToByte( dt.Rows[0]["Cha"] ) );
            pc.addBaseInt( Convert.ToByte( dt.Rows[0]["Intel"] ) );
            pc.addBaseWis( Convert.ToByte( dt.Rows[0]["Wis"] ) );
            int status = SqlUtils.toInt( dt.Rows[0]["Status"] );
            pc.setCurrentWeapon( status );
            int classId = SqlUtils.toInt( dt.Rows[0]["Class"] );
            pc.setClassId( classId );
            pc.setTempCharGfx( classId );
            pc.setGfxId( classId );
            pc.set_sex( SqlUtils.toInt( dt.Rows[0]["Sex"] ) );
            pc.setType( SqlUtils.toInt( dt.Rows[0]["Type"] ) );
            int head = SqlUtils.toInt( dt.Rows[0]["Heading"] );
            if ( head > 7 )
            {
                head = 0;
            }
            pc.setHeading( head );
            /*
             * int locX = resultset.getInt("locX"); int locY =
             * resultset.getInt("locY"); short map =
             * resultset.getShort("MapID"); if (locX < 30000 || locX > 40000 ||
             * locY < 30000 || locY > 40000) { locX = 32564; locY = 32955; } if
             * (map == 70) { locX = 32828; locY = 32848; } // 強制移動 short
             * moveflag = Config.RANGE_RACE_RECOGNIT; if (moveflag != 1) {
             * Random random = new Random(); // 強制移動 int rndmap = 1 +
             * random.nextInt(5); switch (rndmap) { case 1: // skt locX = 33080;
             * locY = 33392; map = 4; break;
             * 
             * case 2: // ti locX = 32580; locY = 32931; map = 0; break;
             * 
             * case 3: // wb locX = 32621; locY = 33169; map = 4; break;
             * 
             * case 4: // kent locX = 33050; locY = 32780; map = 4; break;
             * 
             * case 5: // h locX = 33612; locY = 33268; map = 4; break;
             * 
             * default: // skt locX = 33080; locY = 33392; map = 4; break; } }
             * pc.set_x(locX); pc.set_y(locY); pc.set_map(map);
             */
            pc.setX( SqlUtils.toInt( dt.Rows[0]["locX"] ) );
            pc.setY( SqlUtils.toInt( dt.Rows[0]["locY"] ) );
            pc.setMap( SqlUtils.toShort( dt.Rows[0]["MapID"] ) );
            pc.set_food( SqlUtils.toInt( dt.Rows[0]["Food"] ) );
            pc.setLawful( SqlUtils.toInt( dt.Rows[0]["Lawful"] ) );
            pc.setTitle( Convert.ToString( dt.Rows[0]["Title"] ) );
            pc.setClanid( SqlUtils.toInt( dt.Rows[0]["ClanID"] ) );
            pc.setClanname( Convert.ToString( dt.Rows[0]["Clanname"] ) );
            pc.setClanRank( SqlUtils.toInt( dt.Rows[0]["ClanRank"] ) );
            pc.setBonusStats( SqlUtils.toInt( dt.Rows[0]["BonusStatus"] ) );
            pc.setElixirStats( SqlUtils.toInt( dt.Rows[0]["ElixirStatus"] ) );
            pc.setElfAttr( SqlUtils.toInt( dt.Rows[0]["ElfAttr"] ) );
            pc.set_PKcount( SqlUtils.toInt( dt.Rows[0]["PKcount"] ) );
            pc.setPkCountForElf( SqlUtils.toInt( dt.Rows[0]["PkCountForElf"] ) );
            pc.setExpRes( SqlUtils.toInt( dt.Rows[0]["ExpRes"] ) );
            pc.setPartnerId( SqlUtils.toInt( dt.Rows[0]["PartnerID"] ) );
            pc.setAccessLevel( SqlUtils.toShort( dt.Rows[0]["AccessLevel"] ) );
            if ( pc.getAccessLevel() == 200 )
            {
                pc.setGm( true );
                pc.setMonitor( false );
            }
            else if ( pc.getAccessLevel() == 100 )
            {
                pc.setGm( false );
                pc.setMonitor( true );
            }
            else
            {
                pc.setGm( false );
                pc.setMonitor( false );
            }
            pc.setOnlineStatus( SqlUtils.toInt( dt.Rows[0]["OnlineStatus"] ) );
            pc.setHomeTownId( SqlUtils.toInt( dt.Rows[0]["HomeTownID"] ) );
            pc.setContribution( SqlUtils.toInt( dt.Rows[0]["Contribution"] ) );
            pc.setHellTime( SqlUtils.toInt( dt.Rows[0]["HellTime"] ) );
            pc.setBanned( Convert.ToBoolean( dt.Rows[0]["Banned"] ) );
            pc.setKarma( SqlUtils.toInt( dt.Rows[0]["Karma"] ) );
            pc.setLastPk( SqlUtils.toDateTime( dt.Rows[0]["LastPk"] ) );
            pc.setLastPkForElf( SqlUtils.toDateTime( dt.Rows[0]["LastPkForElf"] ) );
            pc.setDeleteTime( SqlUtils.toDateTime( dt.Rows[0]["DeleteTime"] ) );
            pc.setOriginalStr( SqlUtils.toInt( dt.Rows[0]["OriginalStr"] ) );
            pc.setOriginalCon( SqlUtils.toInt( dt.Rows[0]["OriginalCon"] ) );
            pc.setOriginalDex( SqlUtils.toInt( dt.Rows[0]["OriginalDex"] ) );
            pc.setOriginalCha( SqlUtils.toInt( dt.Rows[0]["OriginalCha"] ) );
            pc.setOriginalInt( SqlUtils.toInt( dt.Rows[0]["OriginalInt"] ) );
            pc.setOriginalWis( SqlUtils.toInt( dt.Rows[0]["OriginalWis"] ) );

            pc.refresh();
            pc.setMoveSpeed( 0 );
            pc.setBraveSpeed( 0 );
            pc.setGmInvis( false );

            _log.finest( "restored char data: " );
            return pc;
        }

        /// <summary>
        /// プレイヤー情報をデータベースから読み込む。
        /// </summary>
        /// <param name="charName"></param>
        /// <returns></returns>
        public L1PcInstance loadCharacter( String charName )
        {/*TTOK*/
            L1PcInstance pc = null;
            pc = restoreCharacter( charName );
            if ( pc == null )
            {
                return null;
            }

            // マップの範囲外ならSKTに移動させる
            L1Map map = L1WorldMap.getInstance().getMap( pc.getMapId() );

            if ( !map.isInMap( pc.getX(), pc.getY() ) )
            {
                pc.setX( 33087 );
                pc.setY( 33396 );
                pc.setMap( (short)4 );
            }

            /*
             * if(l1pcinstance.getClanid() != 0) { L1Clan clan = new L1Clan();
             * ClanTable clantable = new ClanTable(); clan =
             * clantable.getClan(l1pcinstance.getClanname());
             * l1pcinstance.setClanname(clan.GetClanName()); }
             */
            _log.finest( "loadCharacter: " + pc.getName() );

            return pc;
        }

        /// <summary>
        /// 全キャラクターのオンライン状態をオフラインに変更する。
        /// </summary>
        public static void clearOnlineStatus()
        {/*TTOK*/
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                MySqlCommand cmd = new MySqlCommand( "UPDATE characters SET OnlineStatus=0" );
                wrapper.ExecNonQuery( cmd );
            }
        }

        /// <summary>
        /// プレイヤーをオンライン状態にDBを更新する。
        /// </summary>
        /// <param name="pc">プレイヤー情報</param>
        public static void updateOnlineStatus( L1PcInstance pc )
        {/*TTOK*/
            String sqlstr = "UPDATE characters SET OnlineStatus=?1 WHERE objid=?2";
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                MySqlCommand cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.Int24 ).Value = pc.getOnlineStatus();
                cmd.Parameters.Add( "?2", MySqlDbType.Int24 ).Value = pc.getId();
                wrapper.ExecNonQuery( cmd );
            }
        }

        /// <summary>
        /// 結婚パートナーの更新する。(離婚以外で呼ばれていない・・・)
        /// </summary>
        /// <param name="targetId">プレイヤーID</param>
        public static void updatePartnerId( int targetId )
        {/*TTOK*/
            updatePartnerId( targetId, 0 );
        }

        /// <summary>
        /// 結婚パートナーの更新する。(離婚以外で呼ばれていない・・・)
        /// </summary>
        /// <param name="targetId">プレイヤーID</param>
        /// <param name="partnerId">パートナーID</param>
        public static void updatePartnerId( int targetId, int partnerId )
        {/*TTOK*/
            String sqlstr = "UPDATE characters SET PartnerID=?1 WHERE objid=?2";
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                MySqlCommand cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.Int24 ).Value = partnerId;
                cmd.Parameters.Add( "?2", MySqlDbType.Int24 ).Value = targetId;
                wrapper.ExecNonQuery( cmd );
            }
        }

        /// <summary>
        /// プレイヤーステータスを保存する。
        /// </summary>
        /// <param name="pc">プレイヤー情報</param>
        /// <remarks>
        /// 以下のステータスを保存する。
        /// OriginalStr
        /// OriginalCon
        /// OriginalDex
        /// OriginalCha
        /// OriginalInt
        /// OriginalWis
        /// </remarks>
        public static void saveCharStatus( L1PcInstance pc )
        {/*TTOK*/
            String sqlstr = @"UPDATE characters SET 
                                OriginalStr=?1, 
                                OriginalCon=?2, 
                                OriginalDex=?3, 
                                OriginalCha=?4, 
                                OriginalInt=?5, 
                                OriginalWis=?6 
                              WHERE objid=?7";
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                MySqlCommand cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.Int24 ).Value = pc.getBaseStr();
                cmd.Parameters.Add( "?2", MySqlDbType.Int24 ).Value = pc.getBaseCon();
                cmd.Parameters.Add( "?3", MySqlDbType.Int24 ).Value = pc.getBaseDex();
                cmd.Parameters.Add( "?4", MySqlDbType.Int24 ).Value = pc.getBaseCha();
                cmd.Parameters.Add( "?5", MySqlDbType.Int24 ).Value = pc.getBaseInt();
                cmd.Parameters.Add( "?6", MySqlDbType.Int24 ).Value = pc.getBaseWis();
                cmd.Parameters.Add( "?7", MySqlDbType.Int24 ).Value = pc.getId();
                wrapper.ExecNonQuery( cmd );
            }
        }

        /// <summary>
        /// 持ち物をデータベースから読み出す。
        /// </summary>
        /// <param name="pc">プレイヤー情報</param>
        public void restoreInventory( L1PcInstance pc )
        {/*TTOK*/
            pc.getInventory().loadItems();
            pc.getDwarfInventory().loadItems();
            pc.getDwarfForElfInventory().loadItems();
        }

        /// <summary>
        /// キャラクター名がデータベースに存在しているか調べる。
        /// </summary>
        /// <param name="name">キャラクター名</param>
        /// <returns>存在していたらTrue</returns>
        public static bool doesCharNameExist( String name )
        {/*TTOK*/
            bool result = false;
            String sqlstr = "SELECT account_name FROM characters WHERE char_name=?1";
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                MySqlCommand cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.String ).Value = name;
                DataTable dt = wrapper.GetDataTable( cmd );
                if ( dt.Rows.Count >= 1 )
                {
                    result = true;
                }
            }
            return result;
        }

        /// <summary>
        /// 全キャラクターのキャラクター名を読み込む
        /// </summary>
        public void loadAllCharName()
        {/*TTOK*/
            L1CharName cn = null;
            String name = null;
            String sqlstr = "SELECT * FROM characters";
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                DataTable dt = wrapper.GetDataTable( sqlstr );
                foreach ( DataRow item in dt.Rows )
                {
                    cn = new L1CharName();
                    name = item["char_name"] as string;
                    cn.setName( name );
                    cn.setId( SqlUtils.toInt( item["objid"] ) );
                    _charNameList[name] = cn;
                }
            }
        }

        /// <summary>
        /// キャラクター名
        /// </summary>
        /// <param name="charName"></param>
        /// <returns></returns>
        public L1CharName getL1CharName( String charName )
        {/*TTOK*/
            return (L1CharName)_charNameList[charName];
        }
    }
}
